/******************************************************************************* * Copyright (c) 2012-2016 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.everrest.core.impl.provider; import com.google.common.io.ByteStreams; import org.everrest.core.ApplicationContext; import org.everrest.core.impl.FileCollector; import org.everrest.core.provider.EntityProvider; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.util.ByteArrayDataSource; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.Provider; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import static com.google.common.base.Strings.isNullOrEmpty; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; /** * @author andrew00x */ @Provider public class DataSourceEntityProvider implements EntityProvider<DataSource> { @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return type == DataSource.class; } @Override public DataSource readFrom(Class<DataSource> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException { return createDataSource(entityStream, mediaType == null ? null : mediaType.toString()); } @Override public long getSize(DataSource dataSource, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return DataSource.class.isAssignableFrom(type); } @Override public void writeTo(DataSource dataSource, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException { if (httpHeaders.getFirst(CONTENT_TYPE) == null && !isNullOrEmpty(dataSource.getContentType())) { httpHeaders.putSingle(CONTENT_TYPE, dataSource.getContentType()); } try (InputStream in = dataSource.getInputStream()) { ByteStreams.copy(in, entityStream); } } /** * Create DataSource instance dependent entity size. If entity has size less * then <tt>MAX_BUFFER_SIZE</tt> then {@link ByteArrayDataSource} will be * created otherwise {@link MimeFileDataSource} will be created. * * @param entityStream * the {@link InputStream} of the HTTP entity * @param mimeType * media type of data, HTTP header 'Content-type' * @return See {@link DataSource} * @throws IOException * if any i/o errors occurs */ private DataSource createDataSource(InputStream entityStream, String mimeType) throws IOException { boolean overflow = false; byte[] buffer = new byte[8192]; ApplicationContext context = ApplicationContext.getCurrent(); int bufferSize = context.getEverrestConfiguration().getMaxBufferSize(); ByteArrayOutputStream bos = new ByteArrayOutputStream(bufferSize); int bytesNum; while (!overflow && ((bytesNum = entityStream.read(buffer)) != -1)) { bos.write(buffer, 0, bytesNum); if (bos.size() > bufferSize) { overflow = true; } } if (overflow) { File file = FileCollector.getInstance().createFile(); try (OutputStream fos = new FileOutputStream(file)) { bos.writeTo(fos); while ((bytesNum = entityStream.read(buffer)) != -1) { fos.write(buffer, 0, bytesNum); } } return new MimeFileDataSource(file, mimeType); } return new ByteArrayDataSource(bos.toByteArray(), mimeType); } /** FileDataSource with preset media type. */ static class MimeFileDataSource extends FileDataSource { /** Media type of the data. */ private final String mimeType; public MimeFileDataSource(File file, String mimeType) { super(file); this.mimeType = mimeType; } /** Try remove file when object destroyed. {@inheritDoc} */ @Override protected void finalize() throws Throwable { File file = getFile(); if (file.exists()) { file.delete(); } super.finalize(); } @Override public String getContentType() { return mimeType; } } }